﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace Fitts__law_experiment_with_a_wiimote
{

    [Serializable()] public class Experiment
    {
        private String _Name;
        public String Name
        {
            get { return _Name; }
        }
		private bool _aborted = false;
		public bool Aborted
		{
			get { return _aborted; }
		}
		private int _target = 1;
		public int Target
		{
			get { return _target; }
			set { _target = value; }
		}

		public Button TargetButton
		{
			get
			{
				if (Target == 1)
					return b1;
				else
					return b2;
			}
		}
		private bool _missedLastButton = false;
		public bool MissedLastButton
		{
			get { return _missedLastButton; }
		}

		[NonSerialized]
		private Button b1, b2;
        private List<ExperimentEvent> data = new List<ExperimentEvent>();
        private long _startTime = 0;
        public long StartTime
        {
            get { return _startTime; }
        }

        private long _endTime = 0;
        public long EndTime
        {
            get { return _endTime; }
        }

        public bool IsExperimentRunning
        {
			get { return ((StartTime != 0) && (EndTime == 0)); }
        }

		private int _clickCount = 0;
		public int ClickCount
		{
			get { return _clickCount; }
		}
		private int _missedClicksCount = 0;
		public int MissedClicksCount
		{
			get { return _missedClicksCount; }
		}

        public Experiment(String Name, Button primaryButton, Button secondaryButton)
        {
            _Name = Name;
			b1 = primaryButton;
			b2 = secondaryButton;
        }

        public void SaveToFile(String file)
        {
			this.b1 = null;
			this.b2 = null;
            Stream stream = File.Open(file, FileMode.Create, FileAccess.Write);
            BinaryFormatter bformatter = new BinaryFormatter();

            bformatter.Serialize(stream, this);
            stream.Close();
        }

        public static Experiment LoadFromFile(String file)
        {
            Stream stream = File.Open(file, FileMode.Open, FileAccess.Read);
            BinaryFormatter bformatter = new BinaryFormatter();

            Experiment exp = (Experiment)bformatter.Deserialize(stream);
            stream.Close();
            return exp;
        }

        public void Start()
        {
            _startTime = DateTime.Now.Ticks;
        }

        public long getTimestamp()
        {
            return DateTime.Now.Ticks - StartTime;
        }

        public void End()
        {
            _endTime = DateTime.Now.Ticks;
        }

		public void Abort()
		{
			_aborted = true;
			End();
		}

        public List<ExperimentEvent> Events
        {
            get { return data; }
        }

		public ExperimentEvent LastEvent
		{
			get
			{
				if (data.Count > 0)
					return Events[data.Count -1];
				else
					return null;
			}
		}

        public void AddMouseMovedEvent(int mousex, int mousey)
        {
			data.Add(new ExperimentEvent(mousex, mousey, Target, getTimestamp(), buttonToRectangle(TargetButton)));
        }

        public void AddMouseDownEvent(int mousex, int mousey)
        {
			if (LastEvent is Experiment.ExperimentMouseDownEvent)
				return;
			Rectangle r = buttonToRectangle(TargetButton);
			data.Add(new ExperimentMouseDownEvent(mousex, mousey, Target, getTimestamp(), r));
			if (!r.Contains(mousex, mousey))
			{
				_missedClicksCount++;
				_missedLastButton = true;
			}
			else
				_missedLastButton = false;
        }

        public void AddMouseUpEvent(int mousex, int mousey)
        {
			if (LastEvent is Experiment.ExperimentMouseUpEvent)
				return;
			Rectangle r = buttonToRectangle(TargetButton);
            data.Add(new ExperimentMouseUpEvent(mousex, mousey, Target, getTimestamp(), r));
			_clickCount++;
			if (!r.Contains(mousex, mousey))
			{
				if (!MissedLastButton)
					_missedClicksCount++;
				_missedLastButton = true;
			}

        }


        [Serializable()] public class ExperimentEvent
        {
            public int mouseX, mouseY;
			public Rectangle rect;
			public int target = 0;
            public long timestamp;

            public ExperimentEvent(int mx, int my, int target, long timestamp, Rectangle rect)
            {
                mouseX = mx;
                mouseY = my;
				this.rect = rect;
				this.target = target;
                this.timestamp = timestamp;
            }

            public override String ToString()
            {
				return "MouseMoveEvent	" + "mouseX=" + mouseX + "  mouseY=" + mouseY + "	target=" + target + "   timestamp=" + new TimeSpan(timestamp).Seconds + "s:" + new TimeSpan(timestamp).Milliseconds + "ms		" + rect.ToString();
            }
        }

        [Serializable()] public class ExperimentMouseDownEvent : ExperimentEvent
        {
            public ExperimentMouseDownEvent(int mx, int my, int target, long timestamp, Rectangle r) : base(mx, my, target, timestamp , r) {}

            public override String ToString()
            {
				return "MouseDownEvent	" + "mouseX=" + mouseX + "  mouseY=" + mouseY + "	target=" + target + "   timestamp=" + new TimeSpan(timestamp).Seconds + "s:" + new TimeSpan(timestamp).Milliseconds + "ms		" + rect.ToString();
            }
        }

        [Serializable()]
        public class ExperimentMouseUpEvent : ExperimentEvent
        {
            public ExperimentMouseUpEvent(int mx, int my, int target, long timestamp, Rectangle r) : base(mx, my, target, timestamp, r) { }

            public override String ToString()
            {
				return "MouseUpEvent	  " + "mouseX=" + mouseX + "  mouseY=" + mouseY + "	target=" + target + "   timestamp=" + new TimeSpan(timestamp).Seconds + "s:" + new TimeSpan(timestamp).Milliseconds + "ms		" + rect.ToString();
            }
        }

		private static Rectangle buttonToRectangle(Button b)
		{
			Point p = b.PointToScreen(new Point(0, 0));
			return new Rectangle(p.X, p.Y, b.Width, b.Height);
		}
    }
}
